/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014年7月23日
 * V4.0
 */
package com.jphenix.standard.script;

import java.util.List;
import java.util.Map;

import com.jphenix.kernel.script.FileVO;
import com.jphenix.kernel.script.ScriptVO;
import com.jphenix.share.lang.SListMap;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.viewhandler.IViewHandler;

/**
 * 脚本加载管理器
 * 
 * 2018-12-04 开放出，通过脚本VO保存脚本信息
 * 2019-01-16 去掉了获取类加载器(ScriptClassLoad)方法，改为从每个脚本信息类中获取其自己的类加载器
 * 2019-01-21 又恢复了ScriptClassLoad方法，就这么任性。（之前去掉这个方法的思路行不通，具体请看ScriptVO中 注意（190121）
 * 2019-01-28 增加了清除指定脚本运行错误信息方法
 * 2019-06-13 去掉了传参对象中强制指定参数值类型（不灵活）
 * 2019-08-20 增加了模糊搜索指定脚本方法
 * 2019-08-21 增加了通过脚本标题（名字）模糊搜索符合条件的脚本方法
 * 2019-09-30 增加了判断是否存在指定扩展类相对路径方法
 * 2019-10-23 增加了调用脚本方法，新的方法中增加了是否需要返回值参数，用于调用远程脚本
 * 2019-10-30 增加了删除脚本方法，传入删除原因说明信息
 * 2019-12-10 增加了远程调用其它集群服务器上传文件的功能
 * 2020-03-09 为savePathInfo方法增加了返回保存后的文件全路径
 * 2020-07-20 增加了通过文件夹路径获取脚本后缀的方法
 * 2020-09-09 增加了获取脚本类实例时，传入类变量值容器（注意：目前只支持非常驻内存脚本，不支持扩展类脚本）
 * 2021-03-17 废除了默认注册主键
 * 2021-09-11 增加了终止脚本加载器方法
 * 
 * @author 马宝刚
 * 2014年7月23日
 */
@ClassInfo({"2021-09-11 22:02","脚本加载管理器"})
@BeanInfo({"scriptloader"})
public interface IScriptLoader {
	
	/**
	 * 编译前的java文件中顶部的文件标题
	 */
	String CLASS_SOURCE_FILE_TITLE = "代号：凤凰";
	
	/**
	 * 编译前的java文件中顶部的项目版本号
	 */
	String CLASS_SOURCE_FILE_VER = "V5.0";
	
	/**
	 * 编译前的java文件中相关超链接
	 */
	String CLASS_SOURCE_HREF = "http://www.jphenix.org";
	
	/**
	 * 脚本包路径（通过特殊的加载方式，包路径是固定的，
	 * 无需考虑编译后的脚本文件真实保存位置
	 */
	String SCRIPT_PACKAGE = "com.jphenix.scripts";
	
	/**
	 * 脚本包路径的实体路径
	 */
	String SCRIPT_PACKAGE_PATH = "com/jphenix/scripts";
	
	/**
	 * 脚本文件夹说明文件名
	 */
	String SCRIPT_FOLDER_INFO_NAME = "_DEFAULT.xml";
	
	/**
	 * 获取指定的脚本信息
	 * @param scriptID 脚本信息主键
	 * @return 脚本信息类
	 * 2014年7月23日
	 * @author 马宝刚
	 */
	ScriptVO getScriptInfo(String scriptID);
	
	/**
	 * 获取脚本类实例
	 * @param scriptID 脚本类实例
	 * @return 脚本类实例
	 * @throws Exception 异常
	 * 2014年7月23日
	 * @author 马宝刚
	 */
	IScriptBean getScript(String scriptID) throws Exception;
	
	/**
	 * 调用指定的脚本
	 * @param invoker       调用者类实例
	 * @param scriptID		脚本主键
	 * @param inParaMap		传入参数容器
	 * @param invokeType    0普通调用  1调用主服务器  2广播调用  99由集群发起，响应外部服务器调用
	 * @param noReturnValue （用于远程方法调用）如果远程接口没有返回值，在这里标记无需返回值，这样可以提高处理效率
	 * @return				脚本返回值
	 * @throws Exception	异常
	 * 2014年7月29日
	 * @author 马宝刚
	 */
	@SuppressWarnings({"rawtypes"})
	<T> T invokeScript(Object invoker,String scriptID,Map inParaMap,int invokeType,boolean noReturnValue) throws Exception;
	
	/**
	 * 调用指定的脚本
	 * @param invoker 调用者类实例
	 * @param scriptID		脚本主键
	 * @param inParaMap		传入参数容器
	 * @param invokeType    调用方式  0普通调用  1调用调用主服务器  2广播调用
	 * @return				脚本返回值
	 * @throws Exception	异常
	 * 2014年7月29日
	 * @author 马宝刚
	 */
	@SuppressWarnings("rawtypes")
	<T> T invokeScript(Object invoker,String scriptID,Map inParaMap,int invokeType) throws Exception;

	/**
	 * 调用指定的脚本
	 * @param invoker 调用者类实例
	 * @param scriptID		脚本主键
	 * @param inParaMap		传入参数容器
	 * @return				脚本返回值
	 * @throws Exception	异常
	 * 2014年7月29日
	 * @author 马宝刚
	 */
	@SuppressWarnings("rawtypes")
	<T> T invokeScript(Object invoker,String scriptID,Map inParaMap) throws Exception;
	
	/**
	 * 在Action脚本中调用其它脚本（服务），如果其他脚本的传入参数也存在
	 * 于页面提交的参数中，则可以直接从页面提交的参数中提取参数值
	 * 注意：并不是把动作上下文传入目标脚本中。
	 * @param invoker       调用者类实例
	 * @param scriptID      脚本主键
	 * @param ac            动作上下文
	 * @param invokeType    调用方式  0普通调用  1调用调用主服务器  2广播调用  99由集群发起、响应外部服务器调用
	 * @param noReturnValue （用于远程方法调用）如果远程接口没有返回值，在这里标记无需返回值，这样可以提高处理效率
	 * @return              脚本返回值
	 * @throws Exception    异常
	 * 2016年3月21日
	 * @author 马宝刚
	 */
	<T> T invokeScript(Object invoker,String scriptID,IActionContext ac,int invokeType,boolean noReturnValue) throws Exception;
	
	/**
     * 在Action脚本中调用其它脚本（服务），如果其他脚本的传入参数也存在
     * 于页面提交的参数中，则可以直接从页面提交的参数中提取参数值
     * 注意：并不是把动作上下文传入目标脚本中。
     * @param invoker      调用者类实例
     * @param scriptID     脚本主键
     * @param ac           动作上下文
     * @param invokeType   调用方式  0普通调用  1调用调用主服务器  2广播调用
     * @return 脚本返回值
     * @throws Exception 异常
     * 2016年3月21日
     * @author 马宝刚
     */
    <T> T invokeScript(Object invoker,String scriptID,IActionContext ac,int invokeType) throws Exception;
    
	/**
	 * 调用指定脚本
	 * @param invoker 调用者类实例
	 * @param scriptID 		脚本主键
	 * @param invokeType    0普通调用  1调用主服务器  2广播调用
	 * @return				脚本返回值
	 * @throws Exception	异常
	 * 2014年7月29日
	 * @author 马宝刚
	 */
	<T> T invokeScript(Object invoker,String scriptID,int invokeType) throws Exception;
	
	/**
	 * 调用指定的脚本
	 * @param invoker         调用者类实例
	 * @param scriptID        脚本主键
	 * @param invokeType      调用方式  0普通调用  1调用调用主服务器  2广播调用  99由集群发起，响应外部服务器调用
	 * @param noReturenValue  （用于远程方法调用）如果远程接口没有返回值，在这里标记无需返回值，这样可以提高处理效率
	 * @return                脚本返回值
	 * @throws Exception      异常
	 * 2014年7月29日
	 * @author 马宝刚
	 */
	<T> T invokeScript(Object invoker,String scriptID,int invokeType,boolean noReturenValue) throws Exception;
	
	/**
	 * 调用指定脚本
	 * @param invoker 调用者类实例
	 * @param scriptID 		脚本主键
	 * @return				脚本返回值
	 * @throws Exception	异常
	 * 2014年7月29日
	 * @author 马宝刚
	 */
	<T> T invokeScript(Object invoker,String scriptID) throws Exception;
	
	
	/**
	 * 初始化指定脚本（或重新初始化）
	 * @param scriptID		脚本主键
	 * @throws Exception	异常
	 * 2014年7月23日
	 * @author 马宝刚
	 */
	void initScript(String scriptID) throws Exception;
	
	/**
	 * 通过脚本主键终止指定脚本类
	 * @param scriptID 脚本主键
	 * @throws Exception 异常
	 * 2014年7月23日
	 * @author 马宝刚
	 */
	void destoryScript(String scriptID) throws Exception;
	
	/**
	 * 通过源文件相对路径获取源文件内容对象
	 * 
	 * 如果获取的源文件存在错误，可以通过error_msg获取错误信息
	 * 
	 * @param subPath 相对路径
	 * @return 源文件对象
	 * 2014年8月4日
	 * @author 马宝刚
	 */
	IViewHandler getSourceVh(String subPath);
	
	/**
	 * 更新源文件内容，并重新编译
	 * 
	 * 如果保存源文件时存在错误，可以通过error_msg获取错误信息
	 * 
	 * @param sourceVh 即将保存的源文件对象
	 * 2014年8月4日
	 * @author 马宝刚
	 */
	ScriptVO saveSourceVh(IViewHandler sourceVh);
	
	
	/**
	 * 保存脚本信息类
	 * @param sVO 脚本信息类
	 * @return 保存后的脚本信息类
	 * 2014年8月22日
	 * @author 马宝刚
	 */
	void saveSource(ScriptVO sVO);
	
	
	/**
	 * 获取指定路径下的子路径和文件信息序列
	 * @param subPath 指定路径
	 * @return 子路径和信息序列
	 * 2014年8月5日
	 * @author 马宝刚
	 */
	List<FileVO> getPathList(String subPath);
	
	
	/**
	 * 获取全部路径和文件信息
	 * @return 路径和文件信息序列
	 * 2017年2月23日
	 * @author MBG
	 */
	List<FileVO> getAllInfoList();
	
	
	/**
	 * 保存路径信息
	 * @param fileVO 路径信息包
	 * @return       保存后的文件全路径
	 * 2014年8月5日
	 * @author 马宝刚
	 */
	String savePathInfo(FileVO fileVO);
	
	
	/**
	 * 显示当前路径详细信息
	 * @param subPath 指定路径
	 * @return 当前路径相信信息
	 * 2014年8月7日
	 * @author 马宝刚
	 */
	FileVO getPathInfo(String subPath);
	
	
	/**
	 * 重新编译全部源文件
	 * 2014年8月11日
	 * @author 马宝刚
	 */
	void rebuildAll();
	
	
	/**
	 * 删除指定脚本
	 * @param scriptID 脚本信息主键
	 * 2014年8月11日
	 * @author 马宝刚
	 */
	void deleteScript(String scriptID);
	
	/**
	 * 删除指定脚本
	 * @param scriptID 脚本信息主键
	 * @param message  删除原因说明
	 * 2019年10月30日
	 * @author MBG
	 */
	void deleteScript(String scriptID,String message);
	
	/**
	 * 重新编译指定脚本
	 * @param scriptID 脚本信息主键
	 * @param subPath 相对路径
	 * 2014年8月11日
	 * @author 马宝刚
	 */
	void reBuildScript(String scriptID,String subPath);
	
	
	/**
	 * 判断是否存在该脚本类
	 * @param scriptId 脚本信息主键
	 * @return 是否存在该脚本类
	 * 2014年8月14日
	 * @author 马宝刚
	 */
	boolean hasScript(String scriptId);
	
	
	/**
	 * 获取要编译的源文件内容
	 * @param scriptID 脚本主键
	 * @param outError 是否输出错误信息
	 * @return 要编译的源文件内容
	 * 2014年8月15日
	 * @author 马宝刚
	 */
	String getBuildSourceContent(String scriptID,boolean outError);
	
	/**
	 * 获取脚本主键，名称对照容器序列
	 * @return 脚本主键，名称对照容器序列
	 * 2014年8月21日
	 * @author 马宝刚
	 */
	SListMap<String> getScriptInfoList();
	
	
	/**
	 * 通过脚本主键返回对应的脚本标题
	 * @param scriptID 脚本主键
	 * @return 脚本标题
	 * 2014年8月22日
	 * @author 马宝刚
	 */
	String getScriptTitle(String scriptID);
	
	
	/**
	 * 将指定脚本源文件移到指定路径中
	 * @param scriptID 脚本主键
	 * @param subPath 目标相对路径
	 * 2014年8月22日
	 * @author 马宝刚
	 */
	ScriptVO moveSource(String scriptID,String subPath);
	
	/**
	 * 建立全部脚本的备份文件
	 * @return 备份文件路径
	 * 2014年8月25日
	 * @author 马宝刚
	 */
	String createAllSourceBak();
	
	
	/**
	 * 获取指定脚本的备份包
	 * @param scriptID 脚本主键
	 * @return 指定脚本的备份包
	 * 2014年8月25日
	 * @author 马宝刚
	 */
	String getScriptBak(String scriptID);
	
	/**
	 * 获取编译后的脚本源码（支持新增的脚本）
	 * @param sourceVh 即将保存的源文件对象
	 * @param outError 是否输出编译错误信息
	 * @return 代码内容
	 * 2014年8月25日
	 * @author 马宝刚
	 */
	String getBuildSourceContent(IViewHandler sourceVh,boolean outError);
	
	
	/**
	 * 获取包含脚本类实例的脚本信息类
	 * @param scriptID 脚本主键
	 * @return 包含脚本类实例的脚本信息类
	 * @throws Exception 异常
	 * 2014年8月26日
	 * @author 马宝刚
	 */
	ScriptVO getScriptVOObject(String scriptID) throws Exception;
	
	
	   /**
     * 查询指定脚本被其它脚本所引用
     * @param scriptID 脚本主键
     * @return 被用的脚本信息序列
     * 2014年10月24日
     * @author 马宝刚
     */
    List<ScriptVO> getForInclude(String scriptID);
    
    
    /**
     * 获取指定脚本被其它脚本引用的主键
     * @param scriptID 脚本主键
     * @return 被引用的脚本主键序列
     * 2014年10月24日
     * @author 马宝刚
     */
    List<String> getForIncludeScriptIDList(String scriptID);
    
    /**
     * 通过文件夹路径，获取该文件夹中定义的脚本头值
     * ，如果当前文件夹没有定义，取其上一级文件夹中的值
     * ，如果都没有，则为没有头
     * @param path 脚本路径
     * @return 脚本头值
     * 2015年2月13日
     * @author 马宝刚
     */
    String getScriptHeader(String path);
    
    
	/**
	 * 通过文件夹路径，获取从根路径的文件夹雷焦到当前该文件夹中定义的脚本后缀
	 * @param path 脚本路径
	 * @return     获取该文件夹中定义的脚本后缀
	 * 2015年2月13日
	 * @author 马宝刚
	 */
    String getScriptFooter(String path);
    
    /**
     * 获取源文件根路径  要求路径分隔符都为 / 并且末尾没有分隔符
     * @return 源文件根路径
     * 2014年7月8日
     * @author 马宝刚
     */
    String getSourceBasePath();
    
    /**
     * 获取有效的编译后的程序跟路径 要求路径分隔符都为 / 并且末尾没有分隔符
     * @return 有效的编译后的程序跟路径
     * 2014年7月8日
     * @author 马宝刚
     */
    String getClassBasePath();
    
    /**
     * 获取备份根路径
     * @return 备份根路径
     * 2014年8月25日
     * @author 马宝刚
     */
    String getBakBasePath();
    
    /**
     * 获取重复的脚本信息
     * @return 重复的脚本信息
     * 2015年5月15日
     * @author 马宝刚
     */
    List<String> getRepeatScriptInfo();
    
    
    
    /**
     * 执行备份脚本
     * @param scriptID 脚本主键
     * 2015年5月19日
     * @author 马宝刚
     */
    void backupScript(String scriptID);
    
    
    /**
     * 是否没有脚本源码，只有编译后的脚本类
     * @return true没有脚本源码
     * 2015年5月19日
     * @author 马宝刚
     */
    boolean isClassOnly();
    
    
    /**
     * 获取全部脚本主键序列
     * @return 全部脚本主键序列
     * 2015年5月19日
     * @author 马宝刚
     */
    List<String> getScriptIdList();
    
    
    /**
     * 动态加载指定源脚本
     * @param scriptId  脚本主键
     * @param subPath 脚本相对路径 比如  0/0/1
     * @return 加载后的脚本信息
     * 2015年5月21日
     * @author 马宝刚
     */
    ScriptVO loadSource(String scriptId,String subPath);
    
    /**
     * 获取专供脚本使用的序列号生成器
     * 注意：该序列号生成器只能内部使用
     * @return 序列号
     * 2015年11月22日
     * @author 马宝刚
     */
    String getSn();
    
    
    /**
     * 不抛异常方式获取指定脚本类实例（务必确认好是否存在该脚本）
     * @param scriptID 脚本主键
     * @return 脚本类实例
     * 2016年3月17日
     * @author 马宝刚
     */
    IScriptBean getScriptNoException(String scriptID);
    
	/**
	 * 不抛异常方式获取指定脚本类实例（务必确认好是否存在该脚本）
	 * @param scriptID 脚本主键
	 * @param paraMap  在返回类实例之前，将参数容器放入线程会话中，会话主键名为当前脚本主键
	 * @return         脚本类实例
	 * 2016年3月17日
	 * @author 马宝刚
	 */
    IScriptBean getScriptNoException(String scriptID,Map<String,?> paraMap);
    
    
    /**
     * 不抛异常方式（务必确认好是否存在该脚本）
     * @param scriptID 脚本主键
     * @return 脚本类实例
     * 2016年3月17日
     * @author 马宝刚
     */
    IScriptBean script(String scriptID);
    
    /**
     * 设置脚本编号管理器
     * @param snm 脚本编号管理器
     * 2016年4月7日
     * @author 马宝刚
     */
    void setScriptNoManager(IScriptNoManager snm);
    
    /**
     * 获取脚本编号管理器
     * @return 脚本编号管理器
     * 2017年1月12日
     * @author MBG
     */
    IScriptNoManager getScriptNoManager();
    
	/**
	 * 批量编译指定路径下的全部脚本
	 * 
	 * 通常用在保存了一批暂不编译的脚本
	 * 
	 * @param subPath 指定路径
	 * @return 返回编译报错信息
	 * 2016年4月14日
	 * @author MBG
	 */
	String compileScriptByPath(String subPath);
	
	
	/**
	 * 注意：这个方法不能去掉，每个脚本不能使用自己的类加载器
	 *      （即：ScriptVO中内置这个脚本的类加载器），具体原因
	 *      请看ScriptVO中的 注意（190121）
	 *      
	 * 获取脚本的类对象（很少用到，尽量不要用）
	 * @param sVO 类信息容器
	 * @return 脚本类对象
	 * @throws Exception 异常
	 * 2016年7月27日
	 * @author MBG
	 */
	Class<?> getScriptClass(ScriptVO sVO) throws Exception;
	
	/**
	 * 调用指定路由脚本
	 * 
	 * 脚本不是本机脚本，而是通过ProgramRouteFilter程序路由类（分布式运行）调用的远程服务器的脚本
	 * 
	 * @param groupKey            集群群组主键
	 * @param invoker                调用者（用来在日志中显示）
	 * @param scriptID               目标脚本主键
	 * @param inParaMap           传入参数
	 * @param invokeType          调用方式  0普通调用  1调用调用主服务器  2广播调用 （在这个方法中，0也是只调用主服务器，因为路由到目标主服务器中）
	 * @param noReturnValue     是否不需要返回值 
	 *                                        （因为是目标服务器，本机是无法通过脚本信息类判定目标脚本带不带返回值，只能手工强制无需返回值） 
	 *                                         这中情况主要用于反向应触发调用服务，只要通知前置机，无需获取返回值。这样就无需轮询等待返回值，使远程调用
	 * @return            返回值
	 * @throws Exception  异常
	 * 2017年4月21日
	 * @author MBG
	 */
	@SuppressWarnings("rawtypes")
	Object invokeRouteScript(
			String groupKey
			,Object invoker
			,String scriptID
			,Map inParaMap
			,int invokeType
			,boolean noReturnValue) throws Exception;
	
	
	/**
	 * 清除指定脚本的运行错误信息
	 * @param id 脚本的运行错误信息
	 * 2019年1月28日
	 * @author MBG
	 */
	void clearScriptRunError(String id);
	
	
	/**
	 * 设置指定脚本运行时错误信息
	 * 注意：不能直接设置到内置到脚本中的ScriptVO中
	 * ，因为设置到脚本中的ScriptVO是克隆后的，防止人为被修改
	 * @param id        脚本主键
	 * @param ts        发生错误时的时间戳
	 * @param errorMsg  错误信息
	 * 2019年1月28日
	 * @author MBG
	 */
	void setScriptRunError(String id,String ts,String errorMsg);
	
	
	/**
	 * 模糊搜索符合条件的脚本主键
	 * @param scriptID 脚本主键关键字
	 * @return 符合条件的脚本主键脚本名对照容器
	 * 2019年8月20日
	 * @author MBG
	 */
	SListMap<String> findScript(String scriptID);
	
	/**
	 * 通过脚本标题（名字）关键字搜索符合条件的脚本序列容器
	 * @param titleKey 脚本标题（名字）关键字
	 * @return         符合条件的脚本序列容器
	 * 2019年8月21日
	 * @author MBG
	 */
	SListMap<String> findScriptByTitle(String titleKey);
	
	
	/**
	 * 判断是否存在指定扩展类相对路径（多个扩展类相对路径用半角逗号分割）
	 * @return true存在全部扩展类相对路径， false其中或全部相对路径不存在
	 * 
	 * 扩展类相对路径，相对于 /WEB-INF/ext_lib 文件夹 类路径开头不包含/
	 * 
	 * 2019年9月30日
	 * @author MBG
	 */
	boolean hasExtLib(String libSubPath);
	
	
	/**
	 * 执行远程脚本上传文件
	 * 该方法不存在调用本地脚本（本地在后台上传毛，文件就在后台）
	 * @param invoker         调用者类实例
	 * @param scriptID        目标脚本主键
	 * @param params          传入参数 key1=val1&key2=val2
	 * @param uploadNameList  上传文件对象名（目标脚本参数名）序列
	 * @param fileNameList    源文件名序列
	 * @param filePathList    需要上传的文件绝对路径序列
	 * @return                调用返回字符串
	 * @throws Exception      异常
	 * 2019年12月10日
	 * @author MBG
	 */
	String remoteScriptUpload (
			 Object invoker
			,String scriptID
			,String params
			,List<String> uploadNameList
			,List<String> fileNameList
			,List<String> filePathList) throws Exception;
}